<?php
///////////////////////////////////////////////////////////////////////////////////////////////////////
//  这个文件是 JCAT PHP框架的一部，该项目和此文件 均遵循 GNU 自由软件协议
// 
//  Copyleft 2008 JeCat.cn(http://team.JeCat.cn)
//
//
//  JCAT PHP框架 的正式全名是：Jellicle Cat PHP Framework。
//  “Jellicle Cat”出自 Andrew Lloyd Webber的音乐剧《猫》（《Prologue:Jellicle Songs for Jellicle Cats》）。
//  JCAT 是一个开源项目，它像音乐剧中的猫一样自由，你可以毫无顾忌地使用JCAT PHP框架。JCAT 由中国团队开发维护。
//  正在使用的这个版本是：0.4.1 / SVN信息: $Id: class.JCAT_ORM.php 1510 2009-03-23 06:51:00Z alee $
//
//
//
//  相关的链接：
//    [主页] http://jcat.JeCat.cn
//    [下载(HTTP)] http://code.google.com/p/jcat-php/downloads/list
//    [下载(svn)] svn checkout http://jcat-php.googlecode.com/svn/branches/0.4.0/Framework/ JCAT0.4
//    [在线文档] http://jcat.JeCat.cn/document
//    [社区] http://jj.jecat.cn/forum-7-1.html
//  不很相关：
//    [MP3] http://www.google.com/search?q=jellicle+songs+for+jellicle+cats+Andrew+Lloyd+Webber
//    [VCD/DVD] http://www.google.com/search?q=CAT+Andrew+Lloyd+Webber+video
//
///////////////////////////////////////////////////////////////////////////////////////////////////////
/*-- Project Introduce --*/






/**
 * implements a Object(OOP) Relation(DataBase) Mapping
 *
 * @author		alee
 * @access		public
 */
class JCAT_ORM
{
	const RULE_FIELDS = 1 ;
	const RULE_TL_SINGLE = 2 ;
	const RULE_TL_MULTIPLE = 4 ;
	const RULE_TABLE_LINKE = 6 ;
	const RULE_OTHER = 8 ;
	const RULE_ALL = 15 ;

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @return	void
	 */
	public function JCAT_ORM(JCAT_DB $aDB=null)
	{
		$this->aDB = $aDB? $aDB: JCAT_DB::CreateDefaultInstance() ;
	}

	/**
	 * Description
	 *
	 * @access	public
	 * @param	$arrConfig		array
	 * @return	void
	 */
	public function InitFromConfig(array $arrConfig=array())
	{		
		$aDB = $this->GetDB() ;

		// Hub Type
		// -------------------------------------------------
		$sHubType = JCAT_ORMRule_FieldsToProperties::CONFIG_TYPE ;
		if(!isset($arrConfig[$sHubType]))
		{
			throw new JCAT_Exception(JCAT_Language::SentenceEx(
				'ORM(Object Relation Mapping) config array 中缺少必要的内容：hub'
				, 'JCAT', null
			)) ;
		}
		
		$aORMRule = $this->CreateORMRuleFromConf('JCAT_ORMRule_FieldsToProperties',$arrConfig[$sHubType],$aDB) ;
		$this->AddMappingRule($aORMRule) ;
		$sHubTable = $arrConfig[$sHubType]['table'] ;
		
		// other rule type		
		// -------------------------------------------------
		foreach ($arrConfig as $sConfTypeInput=>$arrConfiges)
		{
			$sConfType = strtolower($sConfTypeInput) ;
			if( $sHubType==$sConfType )
			{
				continue ;
			}
			
			if( !self::$arrConfTypeToRuleClass[$sConfType] )
			{
				throw new JCAT_Exception(JCAT_Language::SentenceEx(
					'遇到无效的 ORM(Object Relation Mapping) 配置类型：%s'
					, 'JCAT', null
					, $sConfTypeInput
				)) ;
			}
			
			$sRuleClassName = self::$arrConfTypeToRuleClass[$sConfType] ;
			foreach ($arrConfiges as $arrOneConfig)
			{
				// set hub table for table link
				$arrOneConfig['hubtable'] = $sHubTable ;
				
				$aORMRule = $this->CreateORMRuleFromConf($sRuleClassName,$arrOneConfig,$aDB) ;
				
				// append the rule
				$this->AddMappingRule($aORMRule) ;
			}
		}
	}
	
	/**
	 * Description
	 *
	 * @access	private
	 * @return	void
	 */
	private function CreateORMRuleFromConf($sRuleClassName,array $arrConfig,JCAT_DB $aDB)
	{
		// check 'table' 
		if(empty($arrConfig['table']))
		{
			throw new JCAT_Exception(JCAT_Language::SentenceEx(
				'%s 类型的ORM配置中缺少必要的内容 table'
				, 'JCAT', null, call_user_func(array($sRuleClassName,'GetConfigType'))
			)) ;			
		}
			
		// create rule
		$aDBFactory = $aDB->GetFactory() ;
		$aORMRule = new $sRuleClassName($arrConfig['table'],$aDBFactory) ;
		
		// init the rule
		$aORMRule->InitFromConfig($arrConfig,$aDB) ;
		
		return $aORMRule ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @param	$Parameter
	 * @static
	 * @return	void
	 */
	static public function RegisterConfigType($sType,$sRuleClassname)
	{
		JCAT_ASSERT::ASSERT_ISCLASS($sRuleClassname,array('JCAT_IORMRule','JCAT_IORMRuleConfigurable'),
					JCAT_Language::SentenceEx('参数 $sRuleClassname 必须为实现 JCAT_IORMRule 和 JCAT_IORMRuleConfigurable 接口的类名。','JCAT',null)) ;

		self::$arrConfTypeToRuleClass [ strtolower($sType) ] = $sRuleClassname ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @return	JCAT_DB
	 */
	public function GetDB()
	{
		return $this->aDB ;
	}

	/**
	 * Description
	 *
	 * @access	private
	 * @return	JCAT_ORM_Action
	 */
	private function GetAction()
	{
		if( !$this->aAction )
		{
			$this->aAction = new JCAT_ORM_Action() ;
		}
		
		return $this->aAction ;
	}
	
	/**
	 * add a mapping rule
	 *
	 * @access	public
	 * @param	$aRule	JCAT_IORMRule
	 * @return	void
	 */
	public function AddMappingRule(JCAT_IORMRule $aRule)
	{
		// the mapping of db table fields to model properties
		// for one model one orm ,just have one JCAT_IORMRule_FieldsToProperties rule
		if( JCAT_Global::HasImplementedTo($aRule,'JCAT_IORMRule_FieldsToProperties') )
		{
			$this->aFieldsToPropertiesMappingsRule = $aRule ;
		}
		
		else if( JCAT_Global::HasImplementedTo($aRule,'JCAT_IORMRule_TLSingleAssociation') )
		{
			$this->arrSingleTableLinkMappingRules[] = $aRule ;
		}
		
		else if( JCAT_Global::HasImplementedTo($aRule,'JCAT_IORMRule_TLMultipleAssociation') )
		{
			$this->arrMultiTableLinkMappingRules[] = $aRule ;
		}
		
		else
		{
			$this->arrOtherMappingRules[] = $aRule ;
		}
	}
	
	/**
	 * get field by property
	 *
	 * @access	public
	 * @param	$sPropertyName		string
	 * @return	string
	 */
	public function GetFieldByProperty($sPropertyName)
	{		
		return $this->aFieldsToPropertiesMappingsRule?
					$this->aFieldsToPropertiesMappingsRule->GetFieldByProperty($sPropertyName): null ;
	}

	/**
	 * get field by property
	 *
	 * @access	public
	 * @param	$sFieldName		string
	 * @return	string
	 */
	public function GetPropertyByField($sFieldName)
	{		
		return $this->aFieldsToPropertiesMappingsRule?
					$this->aFieldsToPropertiesMappingsRule->GetPropertyByField($sFieldName): null ;
	}

	/**
	 * get field by property
	 *
	 * @access	public
	 * @param	$sField			string
	 * @param	$aModel			JCAT_IDBModel
	 * @return	string
	 */
	public function GetDataByField($sField,JCAT_IDBModel $aModel)
	{
		$sProperty = $this->GetPropertyByField($sField) ;
		return $sProperty? $aModel->Get($sProperty): null ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @return	string
	 */
	public function GetHubTable()
	{
		return $this->aFieldsToPropertiesMappingsRule?
					$this->aFieldsToPropertiesMappingsRule->GetTableName(): null ;
	}
	
	
	/**
	 * Description
	 *
	 * @access	public
	 * @return	JCAT_IORMRule_FieldsToProperties
	 */
	public function GetFieldsMappingRule()
	{
		return $this->aFieldsToPropertiesMappingsRule ;
	}
	
	/**
	 * get primary key field name
	 *
	 * @access	public
	 * @return	string
	 */
	public function GetPrimaryKey()
	{		
		return $this->aFieldsToPropertiesMappingsRule?
					$this->aFieldsToPropertiesMappingsRule->GetPrimaryKey(): null ;
	}

	/**
	 * get primary key value
	 *
	 * @access	public
	 * @return	string
	 */
	public function GetPrimaryKeyValue(JCAT_IDBModel $aModel)
	{
		$sPrimaryKey = $this->GetPrimaryKey() ;
		return $sPrimaryKey? $this->GetDataByField($sPrimaryKey,$aModel): null ;
	}
	
	
	/**
	 * Description
	 *
	 * @access	public
	 * @param	$sPropertyName		string
	 * @param	$bDesc=true			bool
	 * @return	void
	 */
	public function AddOrderBy($sPropertyName,$bDesc=true)
	{
		if( !$this->aOrderBy )
		{
			$this->aOrderBy = $this->GetDB()->GetFactory()->CreateSubSQLOrder() ;
			
		}
		
		$sFieldName = $this->GetFieldByProperty($sPropertyName) ;
		$this->aOrderBy->AddOrder($sFieldName,$bDesc);
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @return	JCAT_DBSubSQLOrder
	 */
	public function GetSubSQLOrder()
	{
		return $this->aOrderBy ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @return	JCAT_IIterator
	 */
	public function CreateORMRuleIterator($nRuleType=self::RULE_ALL)
	{
		$arrRules = array() ;
	
		if( $nRuleType&self::RULE_FIELDS )
		{
			$arrRules[] = $this->aFieldsToPropertiesMappingsRule ;
		}
		
		if( $nRuleType&self::RULE_TL_SINGLE )
		{
			$arrRules = array_merge($arrRules,$this->arrSingleTableLinkMappingRules) ;
		}
		
		if( $nRuleType&self::RULE_TL_MULTIPLE )
		{
			$arrRules = array_merge($arrRules,$this->arrMultiTableLinkMappingRules) ;
		}
		if( $nRuleType&self::RULE_OTHER )
		{
			$arrRules = array_merge($arrRules,$this->arrOtherMappingRules) ;
		}

		return new JCAT_ArrayIterator($arrRules) ;
	}

	/**
	 * action for insert, update
	 *
	 * @access	public
	 * @param	$aModel		JCAT_DBModel
	 * @return	bool
	 */
	public function Create(JCAT_IDBModel $aModel)
	{
		return $this->GetAction()->Insert($aModel,$this) ;
	}
	
	/**
	 * action for insert, update
	 *
	 * @access	public
	 * @param	$aModel		JCAT_DBModel
	 * @return	bool
	 */
	public function Save(JCAT_IDBModel $aModel)
	{
		return $this->GetAction()->Update($aModel,$this) ;
	}

	
	/**
	 * action for select
	 *
	 * @access	public
	 * @param	$aModel		JCAT_IDBModel
	 * @return	bool
	 */
	public function Load(JCAT_IDBModel $aModel)
	{
		return $this->GetAction()->Select($aModel,$this) ;
	}
	
	/**
	 * action for delete
	 *
	 * @access	public
	 * @param	$aModel		JCAT_DBModel
	 * @return	bool
	 */
	public function Delete(JCAT_DBModel $aModel)
	{
		return $this->GetAction()->Delete($aModel,$this) ;
	}

	/**
	 * Description
	 *
	 * @access	public
	 * @param	$aModel				JCAT_IDBModel
	 * @param	$aRecordSet			JCAT_DBRecordSet
	 * @return	void
	 */
	public function LoadModelByRules(JCAT_IDBModel $aModel,JCAT_DBRecordSet $aRecordSet,JCAT_IIterator $aRuleIterator=null)
	{
		if(!$aRuleIterator)
		{
			$aRuleIterator = $this->CreateORMRuleIterator() ;
		}
		
		// load data to model
		for ( $aRuleIterator->First(); !$aRuleIterator->IsDone(); $aRuleIterator->Next() )
		{
			$aRule = $aRuleIterator->Current() ;
			
			// processing
			$aRule->OnAfterSelect($aModel,$aRecordSet) ;
		}
		
		return true ;
	}
	
	/**
	 * rules of db table fields mapping to model properties
	 * 
	 * @access	private
	 * @var		JCAT_ORMRule_FieldsToProperties
	 */
	private $aFieldsToPropertiesMappingsRule ;
	
	/**
	 * table link one to one association mapping rules
	 * 
	 * @access	private
	 * @var		array
	 */
	private $arrSingleTableLinkMappingRules = array() ;
	
	/**
	 * table link multiple association mapping rules
	 * 
	 * @access	private
	 * @var		array
	 */
	private $arrMultiTableLinkMappingRules = array() ;
	
	/**
	 * other mapping rules
	 * 
	 * @access	private
	 * @var		array
	 */
	private $arrOtherMappingRules = array() ;
	
	/**
	 * Description
	 * 
	 * @access	private
	 * @var		JCAT_DB
	 */
	private $aDB ;
	
	
	/**
	 * Description
	 * 
	 * @access	private
	 * @static
	 * @var		array
	 */
	static private $arrConfTypeToRuleClass = array(
				'fields' => 'JCAT_ORMRule_FieldsToProperties'
				, 'hasone' =>'JCAT_ORMRule_TLHasOne'
				, 'belongsto' =>'JCAT_ORMRule_TLBelongsTo'
				, 'hasmany' =>'JCAT_ORMRule_TLHasMany'
				, 'hasandbelongstomany' =>'JCAT_ORMRule_TLHasAndBelongsToMany'
		) ;
		
	/**
	 * Description
	 * 
	 * @access	private
	 * @var		JCAT_DBSubSQLOrder
	 */
	private $aOrderBy ;


	/**
	 * Description
	 * 
	 * @access	private
	 * @var		JCAT_ORM_Action
	 */
	private $aAction ;
}
?>